import matplotlib.pyplot as plt

import numpy as np
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Conv2D
from paddle.fluid.initializer import NumpyArrayInitializer


with fluid.dygraph.guard():
    #> 创建卷积核 = 参数w
    #? 因为案例简单，仅需要卷积核横向移动即可完成，这里使用了1，0，-1的一维数组
    w = np.array([1, 0, -1], dtype='float32')
    
    #> 将权重参数调整成维度为[cout, cin, kh, kw]的四维张量
    #? 应该是后面的函数仅识别4维列表，所以这里将一维卷积核设置为四维列表
    #? numpy.ndarray的数据类型->初始化卷积参数
    w = w.reshape([1, 1, 1, 3])
    
    #. 创建卷积算子，
    
    #？ 1和2就是在转数据类型
    #> 1.初始化成为：“参数对象”，类型= paddle.fluid.initializer.NumpyArrayInitializer

    
    #> 2.通过
    #@ param_attr的数据类型是 paddle.fluid.param_attr.ParamAttr
    
    #> 初始化方式
    #@ num_channels : 设置输出通道数1，卷积核大小，和
    #@ num_filters  : 卷积核的个数1
    #@ filter_size = [1, 3]表示卷积核竖向1维, 横向3维
    #@ param_attr : 通过参数属性param_attr，指定参数initializer的值为W
    conv = Conv2D(num_channels=1, num_filters=1, filter_size=[1, 3], 
            param_attr = fluid.ParamAttr(
                initializer = NumpyArrayInitializer(value = w )))

    #. 创建输入图片，设置图片50*50的数字矩阵， 等下给卷积算子吃
    img = np.ones([50,50], dtype='float32')
    # 图像右边设置为0
    img[:, 30:] = 0.
    #> 将图片形状调整为[N, C, H, W]的形式
    #@ N Batch size 批次大小
    #@ C 通道数
    #@ H 宽度
    #@ W 高度
    x = img.reshape([1,1,50,50])
    #> 将numpy.ndarray转化成paddle中的tensor
    x = fluid.dygraph.to_variable(x)
    
    #> 使用卷积算子作用在输入图片上
    y = conv(x)
    # 将输出tensor转化为numpy.ndarray
    out = y.numpy()

# 画图
f = plt.subplot(121)
# 设置图片名称
f.set_title('input image', fontsize=15)
# 显示图片
plt.imshow(img, cmap='gray')

# 在画图
f = plt.subplot(122)
# 设置图片名称
f.set_title('output featuremap', fontsize=15)
# 卷积算子Conv2D输出数据形状为[N, C, H, W]形式
# 此处N, C=1，输出数据形状为[1, 1, H, W]，是4维数组
# 但是画图函数plt.imshow画灰度图时，只接受2维数组
# 通过numpy.squeeze函数将大小为1的维度消除
plt.imshow(out.squeeze(), cmap='gray')
# 显示
plt.show()



# 查看卷积层的参数
with fluid.dygraph.guard():
    # 通过 conv.parameters()查看卷积层的参数，返回值是list，包含两个元素
    print(conv.parameters())
    # 查看卷积层的权重参数名字和数值
    print(conv.parameters()[0].name, conv.parameters()[0].numpy())
    # 参看卷积层的偏置参数名字和数值
    print(conv.parameters()[1].name, conv.parameters()[1].numpy())